/*************************************************************************
    > File Name: drvbtn.c
    > Author: klmyt 
    > Created Time: 2025年02月08日 星期六 16时32分16秒
 ************************************************************************/

#include "drvbtn.h"

static unsigned long irqflg = IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING;

typedef struct button {
	int gpio;
	char *name;
	int bindex;
}btn_t;

typedef struct buttonInfo{
	btn_t* pbtns;
	int size;
}btninfo_t;

typedef struct buttonStauts {
	int stauts;
	int index;
    char name[30];
}stauts_t;

typedef struct workparsms{
	btn_t*pbtn;
	struct work_struct work;
}wps_t;


static btn_t* btns;
static wps_t wps;
static wait_queue_head_t wqh;
static struct timer_list timer;
static stauts_t stat;



static void work_handle(struct work_struct* params){
	wps_t *pwps = container_of(params, wps_t, work);
	
	stat.stauts = gpio_get_value(pwps->pbtn->gpio);
	stat.index = pwps->pbtn->bindex;
	strcpy(stat.name, pwps->pbtn->name);

	
	wake_up_interruptible(&wqh);

};

static void timer_handle(unsigned long data){
	schedule_work(&(&wps)->work);
};

static irqreturn_t irq_handle(int irq, void*params){
	
	(&wps)->pbtn = (btn_t*)params;
	
	mod_timer(&timer, msecs_to_jiffies(10));

	return IRQ_HANDLED;
};


static int drvbtn_probe(struct platform_device* pdev){

	btninfo_t* pbtninfo;
	int size;
	int i, irq, irq_rslt;

	pbtninfo = pdev->dev.platform_data;
	btns = pbtninfo->pbtns;
	size = pbtninfo->size;


	for(i = 0; i < size; i++){
		
		gpio_request(btns[i].gpio, btns[i].name);

		gpio_direction_input(btns[i].gpio);

		irq = gpio_to_irq(btns[i].gpio);

		irq_rslt = request_irq(irq, irq_handle, irqflg, btns[i].name, &btns[i]);

		if(irq_rslt) {
			printk("%s: name = %s, request irq error\n", __func__, btns[i].name);
		} else {
			printk("%s: name = %s, reqeust irq success\n", __func__, btns[i].name);
		}

	}

	INIT_WORK(&(&wps)->work, work_handle);

	init_timer(&timer);

	timer.function = timer_handle;

	init_waitqueue_head(&wqh);

	return 0;
};


static int drvbtn_remove(struct platform_device* pdev){
	btninfo_t* pbtninfo;
	int size;
	int i, irq;

	pbtninfo = pdev->dev.platform_data;
	btns = pbtninfo->pbtns;
	size = pbtninfo->size;


	for(i = 0; i < size; i++){
		
		irq = gpio_to_irq(btns[i].gpio);

		free_irq(irq, &btns[i]);

		gpio_free(btns[i].gpio);

	}

	del_timer(&timer);
	
	return 0;
}


static struct platform_driver pd = {
	.driver = {
		.name = "mybtn"
	},
	.probe = drvbtn_probe,
	.remove= drvbtn_remove
};


static struct file_operations fos = {
	.read = drvbtn_staut_read
};

static struct miscdevice misc = {
	.fops = &fos,
	.name = "mybtn",
	.minor = MISC_DYNAMIC_MINOR
};


int drvbtn_init(void){
	platform_driver_register(&pd);
	misc_register(&misc);
	return 0;
};

void drvbtn_exit(void){
	platform_driver_unregister(&pd);
	misc_deregister(&misc);
};

ssize_t drvbtn_staut_read(struct file* f, char __user* buf, size_t count, loff_t*ppos){

	int cpy_rslt;

	wait_queue_t wq;

	init_waitqueue_entry(&wq, current);

	add_wait_queue(&wqh, &wq);

	set_current_state(TASK_INTERRUPTIBLE);
	
	schedule();

	set_current_state(TASK_RUNNING);

	remove_wait_queue(&wqh, &wq);

	if(signal_pending(current)){
		printk("收到kill 信号\n");
		return -ERESTARTSYS;
	} else {

		cpy_rslt = copy_to_user(buf, &stat, sizeof(stauts_t));

		if(cpy_rslt) {
			printk("copy to user error\n");
			return -EFAULT;
		} else {
			printk("copy to user success\n");
		}
	}

	return count;
};

module_init(drvbtn_init);
module_exit(drvbtn_exit);
MODULE_LICENSE("GPL");
